﻿using System;
using System.Threading.Tasks;
using eLoop;

namespace Beeflys
{
    public abstract partial class AActor : IRunnable
    {
        internal async Task Deliver(ActorContext context)
        {
            if (!this.IsActive)
            {
                return;
            }

            try
            {
                await this.mailbox.WriteAsync(context);
            }
            catch (Exception ex)
            {
                this.Logger.Error(ex.ToString());
            }
        }
        
        async void IRunnable.Execute(object state)
        {
            await this.OnReceiveMessage();
        }
        
        private async Task OnReceiveMessage()
        {
            while(this.IsActive)
            {
                var context = await this.mailbox.ReadAsync();
                if (context == null) return;
                await this.RouteAsync(context);
            }
        }
        
        private async Task RouteAsync(ActorContext context)
        {
            //Console.WriteLine(">>" + context.Request.ToString());
            try
            {
                if (await this.TryParseSystemNotify(context)) return;
                if (await this.TryParseChildNotify(context)) return;
                //
                // for (int i = 0; i < this.parsers.Count; i++)
                // {
                //     var parser = this.parsers[i];
                //     if (await parser(context)) return;
                // }
                //
                await this.OnReceivedAsync(context);
            }
            catch (Exception)
            {
                throw;
            }
        }

        protected virtual Task OnReceivedAsync(IActorContext context)
        {
            return Task.CompletedTask;
        }


        private async ValueTask<bool> TryParseChildNotify(IActorContext context)
        {
            if (context.Request is AddChildren addChildren)
            {
                Type actorType = addChildren.ActorType;
                string host = addChildren.Host;
                string actorName = addChildren.ActorName;
                
                var actorRef = await actorSystem.AddActorAsync(actorType, host, actorName);
                context.Sponsor.Respond(actorRef, ActorOpencode.SUCCEED);
                return true;
            }
            
            if (context.Request is GetChildren getChildren)
            {
                var actorRef = await this.GetChildAsync(getChildren.ActorName);
                context.Sponsor.Respond(actorRef, ActorOpencode.SUCCEED);
                return true;
            }
            
            if (context.Request is DelChildren delChildren)
            {
                var succeed = await this.DelChildAsync(delChildren.ActorName);
                context.Sponsor.Respond(succeed, ActorOpencode.SUCCEED);
                return true;
            }

            return false;
        }

        private async ValueTask<bool> TryParseSystemNotify(IActorContext context)
        {
            var systemNotify = context.Request as SystemNotify;
            if (systemNotify == null) return false;

            switch (systemNotify.type)
            {
               case SystemNotifyType.AWAKE:
                   if (this is IAwake awaker)
                       await awaker.OnAwakeAsync();
                   
                   context.Sponsor.Respond(null, ActorOpencode.SUCCEED);
                   break;
               case SystemNotifyType.DESTROY:
                   await this.InternalDestroyAsync();

                    if (this is IDestroy destroyer)
                        await destroyer.OnDestroyAsync();
                    context.Sponsor.Respond(null, ActorOpencode.SUCCEED);
                   break;
               case SystemNotifyType.START:
                   if (systemNotify is SystemArgsNotify argsNotify)
                   {
                        if (argsNotify.arg is FuncAsyncDelegate startAsyncNotify)
                        {
                            var finish = await startAsyncNotify?.Invoke(this);
                            context.Sponsor.Respond(finish, ActorOpencode.SUCCEED);
                        }
                        else
                        {
                            context.Sponsor.Respond(false, ActorOpencode.SUCCEED);
                        }
                   }

                    context.Sponsor.Respond(false, ActorOpencode.SUCCEED);
                    break;
               default:
                   throw new ArgumentOutOfRangeException();
            }

            return true;
        }
        
        
    }
}